#include "matrixmgr.hh"

#include "morepacket.hh"

CLICK_DECLS

MatrixMgr::~MatrixMgr() {
    if(_encoded) {
        delete _encoded;
    }
    _mIO.clear();
    clear();
}

bool MatrixMgr::addI(CodedBlock *cb) {
    if(_mIO.add(new CodedBlock(*cb))) {
        int f = _rank;
        add(f, cb);
        draw(f);
        return true;
    }
    return false;
}

/**
 * Encode a new block
 * plain random iid with uniform prob.
 * if p is given it is reused, but it should point at more_block
 */
WritablePacket * MatrixMgr::prepare(WritablePacket *p) {
    if(_encoded) 
        return _encoded->prepare();
    _encoded = new CodedBlock(_k, _datasize, p);

    if(empty())
        return _encoded->prepare();
    for(int i = 0; i < _k; ++i) {
        if(_blocks[i]) {
            uchar c = CodedBlock::random_range(0, GF_MAX);
            if(c)
                _encoded->add(*_blocks[i], c);
        }
    }
    return _encoded->prepare();
}

/**
 * Wrap up the packet and steal it.
 * This code is nasty only because of a rare border case, 
 * when the random code vector turns out to be all zeroes.
 */
WritablePacket * MatrixMgr::take() {
    if(!_encoded)
        prepare();

    // check if we would hit 0
    // probability of failure <= (1/q)^k
    if(_encoded->degree() == 0) {
        // generate coefficients and test against 0, then actually encode
        int tries = 0;
        uchar coeff[_k];
        for(;/*ever*/;) {
            click_chatter("Warning: re-encoding on-demand");
            if(++tries > 5) {
                click_chatter("FAILED TO ENCODE A NON-ZERO PACKET %d", rank());
                break;
            }
            bool nonzero = false;
            for(int i = 0; i < _k; ++i) {
                coeff[i] = 0;
                if(_blocks[i]) {
                    uchar c = CodedBlock::random_range(0, GF_MAX);
                    if(c) {
                        coeff[i] = c;
                        nonzero = true;
                    }
                }
            }
            if(nonzero) {
                // make new _encoded
                delete _encoded;
                _encoded = new CodedBlock(_k, _datasize);
                for(int i = 0; i < _k; ++i) {
                    if(coeff[i]) {
                        _encoded->add(*_blocks[i], coeff[i]);
                        assert(_encoded->degree());
                    }
                }
                break;
            }
        }
    }

    // NEVER EVER SEND AN EMPTY PACKET!
    assert(_encoded->degree());
    WritablePacket* p = _encoded->take();
    delete _encoded; _encoded = 0;
    return p;
}

void MatrixMgr::draw(int i) {
    assert(_blocks[i]);
    //assert(_encoded);
    if(!_encoded) {
        click_chatter("WARNING: packet not prepared\n");
        prepare();
    }
    // HACK! non-zero coefficients!
    uchar c = CodedBlock::random_range(0, GF_MAX);
    if(c) {
        _encoded->add(*_blocks[i], c);
        assert(_encoded->degree());
    }
}

CLICK_ENDDECLS
ELEMENT_PROVIDES(MatrixMgr)

